﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using Ionic.Zlib;

namespace Terraria.ModLoader.IO
{
	// Token: 0x020002E8 RID: 744
	public static class TagIO
	{
		// Token: 0x06001CAB RID: 7339 RVA: 0x0001462F File Offset: 0x0001282F
		private static TagIO.PayloadHandler GetHandler(int id)
		{
			if (id < 1 || id >= TagIO.PayloadHandlers.Length)
			{
				throw new IOException("Invalid NBT payload id: " + id);
			}
			return TagIO.PayloadHandlers[id];
		}

		// Token: 0x06001CAC RID: 7340 RVA: 0x00436BA8 File Offset: 0x00434DA8
		private static int GetPayloadId(Type t)
		{
			int result;
			if (TagIO.PayloadIDs.TryGetValue(t, out result))
			{
				return result;
			}
			if (typeof(IList).IsAssignableFrom(t))
			{
				return 9;
			}
			throw new IOException(string.Format("Invalid NBT payload type '{0}'", t));
		}

		// Token: 0x06001CAD RID: 7341 RVA: 0x00436BEC File Offset: 0x00434DEC
		public static object Serialize(object value)
		{
			Type type = value.GetType();
			TagSerializer tagSerializer;
			if (TagSerializer.TryGetSerializer(type, out tagSerializer))
			{
				return tagSerializer.Serialize(value);
			}
			if (TagIO.GetPayloadId(type) != 9)
			{
				return value;
			}
			Type type2 = type.GetGenericArguments()[0];
			if (TagSerializer.TryGetSerializer(type2, out tagSerializer))
			{
				return tagSerializer.SerializeList((IList)value);
			}
			if (TagIO.GetPayloadId(type2) != 9)
			{
				return value;
			}
			IList<IList> list = (value as IList<IList>) ?? ((IList)value).Cast<IList>().ToList<IList>();
			for (int i = 0; i < list.Count; i++)
			{
				list[i] = (IList)TagIO.Serialize(list[i]);
			}
			return list;
		}

		// Token: 0x06001CAE RID: 7342 RVA: 0x0001465C File Offset: 0x0001285C
		public static T Deserialize<T>(object tag)
		{
			if (tag is T)
			{
				return (T)((object)tag);
			}
			return (T)((object)TagIO.Deserialize(typeof(T), tag));
		}

		// Token: 0x06001CAF RID: 7343 RVA: 0x00436C98 File Offset: 0x00434E98
		public static object Deserialize(Type type, object tag)
		{
			if (type.IsInstanceOfType(tag))
			{
				return tag;
			}
			TagSerializer tagSerializer;
			if (TagSerializer.TryGetSerializer(type, out tagSerializer))
			{
				if (tag == null)
				{
					tag = TagIO.Deserialize(tagSerializer.TagType, null);
				}
				return tagSerializer.Deserialize(tag);
			}
			if (tag == null)
			{
				if (type.GetGenericArguments().Length == 0)
				{
					return TagIO.GetHandler(TagIO.GetPayloadId(type)).Default();
				}
				if (type.GetGenericTypeDefinition() == typeof(Nullable<>))
				{
					return null;
				}
			}
			if ((tag == null || tag is IList) && type.GetGenericArguments().Length == 1)
			{
				Type type2 = type.GetGenericArguments()[0];
				Type type3 = typeof(List<>).MakeGenericType(new Type[]
				{
					type2
				});
				if (type.IsAssignableFrom(type3))
				{
					if (tag == null)
					{
						return type3.GetConstructor(new Type[0]).Invoke(new object[0]);
					}
					if (TagSerializer.TryGetSerializer(type2, out tagSerializer))
					{
						return tagSerializer.DeserializeList((IList)tag);
					}
					IList list = (IList)tag;
					IList list2 = (IList)type3.GetConstructor(new Type[]
					{
						typeof(int)
					}).Invoke(new object[]
					{
						list.Count
					});
					foreach (object tag2 in list)
					{
						list2.Add(TagIO.Deserialize(type2, tag2));
					}
					return list2;
				}
			}
			if (tag == null)
			{
				throw new IOException(string.Format("Invalid NBT payload type '{0}'", type));
			}
			throw new InvalidCastException(string.Format("Unable to cast object of type '{0}' to type '{1}'", tag.GetType(), type));
		}

		// Token: 0x06001CB0 RID: 7344 RVA: 0x00014682 File Offset: 0x00012882
		public static T Clone<T>(T o)
		{
			return (T)((object)TagIO.GetHandler(TagIO.GetPayloadId(o.GetType())).Clone(o));
		}

		// Token: 0x06001CB1 RID: 7345 RVA: 0x00436E48 File Offset: 0x00435048
		public static object ReadTag(BinaryReader r, out string name)
		{
			int num = (int)r.ReadByte();
			if (num == 0)
			{
				name = null;
				return null;
			}
			name = TagIO.StringHandler.reader(r);
			return TagIO.PayloadHandlers[num].Read(r);
		}

		// Token: 0x06001CB2 RID: 7346 RVA: 0x00436E84 File Offset: 0x00435084
		public static void WriteTag(string name, object tag, BinaryWriter w)
		{
			int payloadId = TagIO.GetPayloadId(tag.GetType());
			w.Write((byte)payloadId);
			TagIO.StringHandler.writer(w, name);
			TagIO.PayloadHandlers[payloadId].Write(w, tag);
		}

		// Token: 0x06001CB3 RID: 7347 RVA: 0x00436EC4 File Offset: 0x004350C4
		public static TagCompound FromFile(string path, bool compressed = true)
		{
			TagCompound result;
			try
			{
				using (Stream stream = new FileStream(path, FileMode.Open, FileAccess.Read))
				{
					result = TagIO.FromStream(stream, compressed);
				}
			}
			catch (IOException innerException)
			{
				throw new IOException("Failed to read NBT file: " + path, innerException);
			}
			return result;
		}

		// Token: 0x06001CB4 RID: 7348 RVA: 0x000146AB File Offset: 0x000128AB
		public static TagCompound FromStream(Stream stream, bool compressed = true)
		{
			if (compressed)
			{
				stream = new GZipStream(stream, 1);
			}
			return TagIO.Read(new BigEndianReader(stream));
		}

		// Token: 0x06001CB5 RID: 7349 RVA: 0x00436F20 File Offset: 0x00435120
		public static TagCompound Read(BinaryReader reader)
		{
			string text;
			object obj = TagIO.ReadTag(reader, out text);
			if (!(obj is TagCompound))
			{
				throw new IOException("Root tag not a TagCompound");
			}
			return (TagCompound)obj;
		}

		// Token: 0x06001CB6 RID: 7350 RVA: 0x00436F50 File Offset: 0x00435150
		public static void ToFile(TagCompound root, string path, bool compress = true)
		{
			try
			{
				using (Stream stream = new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write))
				{
					TagIO.ToStream(root, stream, compress);
				}
			}
			catch (IOException innerException)
			{
				throw new IOException("Failed to read NBT file: " + path, innerException);
			}
		}

		// Token: 0x06001CB7 RID: 7351 RVA: 0x000146C4 File Offset: 0x000128C4
		public static void ToStream(TagCompound root, Stream stream, bool compress = true)
		{
			if (compress)
			{
				stream = new GZipStream(stream, 0, true);
			}
			TagIO.Write(root, new BigEndianWriter(stream));
			if (compress)
			{
				stream.Close();
			}
		}

		// Token: 0x06001CB8 RID: 7352 RVA: 0x000146E8 File Offset: 0x000128E8
		public static void Write(TagCompound root, BinaryWriter writer)
		{
			TagIO.WriteTag("", root, writer);
		}

		// Token: 0x040018D0 RID: 6352
		private static readonly TagIO.PayloadHandler[] PayloadHandlers = new TagIO.PayloadHandler[]
		{
			null,
			new TagIO.PayloadHandler<byte>((BinaryReader r) => r.ReadByte(), delegate(BinaryWriter w, byte v)
			{
				w.Write(v);
			}),
			new TagIO.PayloadHandler<short>((BinaryReader r) => r.ReadInt16(), delegate(BinaryWriter w, short v)
			{
				w.Write(v);
			}),
			new TagIO.PayloadHandler<int>((BinaryReader r) => r.ReadInt32(), delegate(BinaryWriter w, int v)
			{
				w.Write(v);
			}),
			new TagIO.PayloadHandler<long>((BinaryReader r) => r.ReadInt64(), delegate(BinaryWriter w, long v)
			{
				w.Write(v);
			}),
			new TagIO.PayloadHandler<float>((BinaryReader r) => r.ReadSingle(), delegate(BinaryWriter w, float v)
			{
				w.Write(v);
			}),
			new TagIO.PayloadHandler<double>((BinaryReader r) => r.ReadDouble(), delegate(BinaryWriter w, double v)
			{
				w.Write(v);
			}),
			new TagIO.ClassPayloadHandler<byte[]>((BinaryReader r) => r.ReadBytes(r.ReadInt32()), delegate(BinaryWriter w, byte[] v)
			{
				w.Write(v.Length);
				w.Write(v);
			}, (byte[] v) => (byte[])v.Clone(), () => new byte[0]),
			new TagIO.ClassPayloadHandler<string>((BinaryReader r) => Encoding.UTF8.GetString(r.ReadBytes((int)r.ReadInt16())), delegate(BinaryWriter w, string v)
			{
				byte[] bytes = Encoding.UTF8.GetBytes(v);
				w.Write((short)bytes.Length);
				w.Write(bytes);
			}, (string v) => v, () => ""),
			new TagIO.ClassPayloadHandler<IList>((BinaryReader r) => TagIO.GetHandler((int)r.ReadByte()).ReadList(r, r.ReadInt32()), delegate(BinaryWriter w, IList v)
			{
				int payloadId;
				try
				{
					payloadId = TagIO.GetPayloadId(v.GetType().GetGenericArguments()[0]);
				}
				catch (IOException)
				{
					throw new IOException("Invalid NBT list type: " + v.GetType());
				}
				w.Write((byte)payloadId);
				w.Write(v.Count);
				TagIO.PayloadHandlers[payloadId].WriteList(w, v);
			}, delegate(IList v)
			{
				IList result;
				try
				{
					result = TagIO.GetHandler(TagIO.GetPayloadId(v.GetType().GetGenericArguments()[0])).CloneList(v);
				}
				catch (IOException)
				{
					throw new ArgumentException("Invalid NBT list type: " + v.GetType());
				}
				return result;
			}, null),
			new TagIO.ClassPayloadHandler<TagCompound>(delegate(BinaryReader r)
			{
				TagCompound tagCompound = new TagCompound();
				string key;
				object value;
				while ((value = TagIO.ReadTag(r, out key)) != null)
				{
					tagCompound.Set(key, value);
				}
				return tagCompound;
			}, delegate(BinaryWriter w, TagCompound v)
			{
				foreach (KeyValuePair<string, object> keyValuePair in v)
				{
					if (keyValuePair.Value != null)
					{
						TagIO.WriteTag(keyValuePair.Key, keyValuePair.Value, w);
					}
				}
				w.Write(0);
			}, (TagCompound v) => (TagCompound)v.Clone(), () => new TagCompound()),
			new TagIO.ClassPayloadHandler<int[]>(delegate(BinaryReader r)
			{
				int[] array = new int[r.ReadInt32()];
				for (int i = 0; i < array.Length; i++)
				{
					array[i] = r.ReadInt32();
				}
				return array;
			}, delegate(BinaryWriter w, int[] v)
			{
				w.Write(v.Length);
				foreach (int value in v)
				{
					w.Write(value);
				}
			}, (int[] v) => (int[])v.Clone(), () => new int[0])
		};

		// Token: 0x040018D1 RID: 6353
		private static readonly Dictionary<Type, int> PayloadIDs = Enumerable.Range(1, TagIO.PayloadHandlers.Length - 1).ToDictionary((int i) => TagIO.PayloadHandlers[i].PayloadType);

		// Token: 0x040018D2 RID: 6354
		private static TagIO.PayloadHandler<string> StringHandler = (TagIO.PayloadHandler<string>)TagIO.PayloadHandlers[8];

		// Token: 0x020002E9 RID: 745
		private abstract class PayloadHandler
		{
			// Token: 0x170001DB RID: 475
			// (get) Token: 0x06001CBA RID: 7354
			public abstract Type PayloadType { get; }

			// Token: 0x06001CBB RID: 7355
			public abstract object Default();

			// Token: 0x06001CBC RID: 7356
			public abstract object Read(BinaryReader r);

			// Token: 0x06001CBD RID: 7357
			public abstract void Write(BinaryWriter w, object v);

			// Token: 0x06001CBE RID: 7358
			public abstract IList ReadList(BinaryReader r, int size);

			// Token: 0x06001CBF RID: 7359
			public abstract void WriteList(BinaryWriter w, IList list);

			// Token: 0x06001CC0 RID: 7360
			public abstract object Clone(object o);

			// Token: 0x06001CC1 RID: 7361
			public abstract IList CloneList(IList list);
		}

		// Token: 0x020002EA RID: 746
		private class PayloadHandler<T> : TagIO.PayloadHandler
		{
			// Token: 0x06001CC3 RID: 7363 RVA: 0x000146F6 File Offset: 0x000128F6
			public PayloadHandler(Func<BinaryReader, T> reader, Action<BinaryWriter, T> writer)
			{
				this.reader = reader;
				this.writer = writer;
			}

			// Token: 0x170001DC RID: 476
			// (get) Token: 0x06001CC4 RID: 7364 RVA: 0x00013FDB File Offset: 0x000121DB
			public override Type PayloadType
			{
				get
				{
					return typeof(T);
				}
			}

			// Token: 0x06001CC5 RID: 7365 RVA: 0x0001470C File Offset: 0x0001290C
			public override object Read(BinaryReader r)
			{
				return this.reader(r);
			}

			// Token: 0x06001CC6 RID: 7366 RVA: 0x0001471F File Offset: 0x0001291F
			public override void Write(BinaryWriter w, object v)
			{
				this.writer(w, (T)((object)v));
			}

			// Token: 0x06001CC7 RID: 7367 RVA: 0x0043724C File Offset: 0x0043544C
			public override IList ReadList(BinaryReader r, int size)
			{
				List<T> list = new List<T>(size);
				for (int i = 0; i < size; i++)
				{
					list.Add(this.reader(r));
				}
				return list;
			}

			// Token: 0x06001CC8 RID: 7368 RVA: 0x00014733 File Offset: 0x00012933
			public override void WriteList(BinaryWriter w, IList list)
			{
				this.WriteList(w, (IList<T>)list);
			}

			// Token: 0x06001CC9 RID: 7369 RVA: 0x00437280 File Offset: 0x00435480
			public void WriteList(BinaryWriter w, IList<T> list)
			{
				foreach (T arg in list)
				{
					this.writer(w, arg);
				}
			}

			// Token: 0x06001CCA RID: 7370 RVA: 0x0000EE72 File Offset: 0x0000D072
			public override object Clone(object o)
			{
				return o;
			}

			// Token: 0x06001CCB RID: 7371 RVA: 0x00014742 File Offset: 0x00012942
			public override IList CloneList(IList list)
			{
				return this.CloneList((IList<T>)list);
			}

			// Token: 0x06001CCC RID: 7372 RVA: 0x00014750 File Offset: 0x00012950
			public virtual IList CloneList(IList<T> list)
			{
				return new List<T>(list);
			}

			// Token: 0x06001CCD RID: 7373 RVA: 0x004372D0 File Offset: 0x004354D0
			public override object Default()
			{
				return default(T);
			}

			// Token: 0x040018D3 RID: 6355
			internal Func<BinaryReader, T> reader;

			// Token: 0x040018D4 RID: 6356
			internal Action<BinaryWriter, T> writer;
		}

		// Token: 0x020002EB RID: 747
		private class ClassPayloadHandler<T> : TagIO.PayloadHandler<T> where T : class
		{
			// Token: 0x06001CCE RID: 7374 RVA: 0x00014758 File Offset: 0x00012958
			public ClassPayloadHandler(Func<BinaryReader, T> reader, Action<BinaryWriter, T> writer, Func<T, T> clone, Func<T> makeDefault = null) : base(reader, writer)
			{
				this.clone = clone;
				this.makeDefault = makeDefault;
			}

			// Token: 0x06001CCF RID: 7375 RVA: 0x00014771 File Offset: 0x00012971
			public override object Clone(object o)
			{
				return this.clone((T)((object)o));
			}

			// Token: 0x06001CD0 RID: 7376 RVA: 0x00014789 File Offset: 0x00012989
			public override IList CloneList(IList<T> list)
			{
				return list.Select(this.clone).ToList<T>();
			}

			// Token: 0x06001CD1 RID: 7377 RVA: 0x0001479C File Offset: 0x0001299C
			public override object Default()
			{
				return this.makeDefault();
			}

			// Token: 0x040018D5 RID: 6357
			private Func<T, T> clone;

			// Token: 0x040018D6 RID: 6358
			private Func<T> makeDefault;
		}
	}
}
